/**
 * For more information see the head comment within the 'java.g' grammar file
 * that defines the input for this tree grammar.
 *
 * BSD licence
 * 
 * Copyright (c) 2007-2008 by HABELITZ Software Developments
 *
 * All rights reserved.
 * 
 * http://www.habelitz.com
 *
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  1. Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *  3. The name of the author may not be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY HABELITZ SOFTWARE DEVELOPMENTS ('HSD') ``AS IS'' 
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
 * ARE DISCLAIMED. IN NO EVENT SHALL 'HSD' BE LIABLE FOR ANY DIRECT, INDIRECT, 
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
 
 /*Tree parser grammar for the first passage over the java source tree
 * 
 * 1. Passage determins the completly qualified name of this source's type
 * Information will be used in follwing passes to construct completely qualified names of global members
 *
 */
tree grammar JavaASTThirdPass;

options {
    backtrack = true; 
    memoize = true;
    tokenVocab = Java;
    ASTLabelType = CommonTree;
}


@treeparser::header {
	package patternmining.model.construction;
	import java.util.LinkedList;
}

@treeparser::members {
    
    boolean mMessageCollectionEnabled = false;
    private boolean mHasErrors = false;
    List<String> mMessages;

    /**
     *  Switches error message collection on or of.
     *
     *  The standard destination for parser error messages is <code>System.err</code>.
     *  However, if <code>true</code> gets passed to this method this default
     *  behaviour will be switched off and all error messages will be collected
     *  instead of written to anywhere.
     *
     *  The default value is <code>false</code>.
     *
     *  @param pNewState  <code>true</code> if error messages should be collected.
     */
    public void enableErrorMessageCollection(boolean pNewState) {
        mMessageCollectionEnabled = pNewState;
        if (mMessages == null && mMessageCollectionEnabled) {
            mMessages = new ArrayList<String>();
        }
    }
    
    /**
     *  Collects an error message or passes the error message to <code>
     *  super.emitErrorMessage(...)</code>.
     *
     *  The actual behaviour depends on whether collecting error messages
     *  has been enabled or not.
     *
     *  @param pMessage  The error message.
     */
     @Override
    public void emitErrorMessage(String pMessage) {
        if (mMessageCollectionEnabled) {
            mMessages.add(pMessage);
        } else {
            super.emitErrorMessage(pMessage);
        }
    }
    
    /**
     *  Returns collected error messages.
     *
     *  @return  A list holding collected error messages or <code>null</code> if
     *           collecting error messages hasn't been enabled. Of course, this
     *           list may be empty if no error message has been emited.
     */
    public List<String> getMessages() {
        return mMessages;
    }
    
    /**
     *  Tells if parsing a Java source has caused any error messages.
     *
     *  @return  <code>true</code> if parsing a Java source has caused at least one error message.
     */
    public boolean hasErrors() {
        return mHasErrors;
    }
    
    
    /** Members used in the extraction of the source information relevant to the program graph construction
    *
    * Atributes
    */
    private String packagePrefix;
	private String thisTypeName;
	private Scope currentScope;
	String superType;
	CustomExceptionProcessor exceptionProcessor;
	
	private TypeNameQualifier typeNameQualifier;
	private AttributeTypeResolver attributeResolver;
	private AttributeTypeResolver currentLocalVars;
	private MethodNameResolver methodResolver;
	private ModelData modelData;
	private MethodCallAnalyzer methodCallAnalyzer;
	
	/* Alternative constructor to pass the shared ModelData object to this tree parser
	*/
	public JavaASTThirdPass(TreeNodeStream input, TypeNameQualifier typeNameQualifier, AttributeTypeResolver attributeResolver, MethodNameResolver methodResolver  ){
		this(input);
		this.typeNameQualifier = typeNameQualifier;
		this.attributeResolver = attributeResolver;
		this.methodResolver = methodResolver;
		this.modelData = new ModelData();
		exceptionProcessor = new CustomExceptionProcessor();
		this.methodCallAnalyzer = new MethodCallAnalyzer( methodResolver, attributeResolver );
	}
    
    /*Method to obtain the collected Data
    */    
    public ModelData getModelData(){
    	return modelData;
    }
    
    enum Scope{
    	CLASS, METHODBLOCK, CONSTRUCTOR, INDEF;
    }
    
    class CustomExceptionProcessor{
    
    	public void handleCustomException(Exception e){
    		System.out.println(e.toString());
    	}
    	
    }
    
}

// Starting point for parsing a Java file.
javaSource
	@init{ currentScope = Scope.INDEF; }
    :   ^(JAVA_SOURCE annotationList packageDeclaration? importDeclaration* typeDeclaration*)
    ;

packageDeclaration
	:   ^(PACKAGE temp=qualifiedIdentifier)
		{ packagePrefix = $temp.text; }
	;
    
importDeclaration
    :   ^(IMPORT STATIC? qualifiedIdentifier DOTSTAR?)
    ;
    
typeDeclaration
    :   ^(CLASS modifierList temp=IDENT
    	{
    		thisTypeName = packagePrefix + "." + $temp.text;
    		methodCallAnalyzer.setTypeNameOfOwnerInstance( thisTypeName );
    		modelData.addVertexData( new VertexData( thisTypeName, $temp.line, $temp.pos, BPSLTermType.CLASS) );
    	}
    	genericTypeParameterList? extendsClause? implementsClause? classTopLevelScope)
    	  
    |   ^(INTERFACE modifierList temp=IDENT
    	{
    		thisTypeName = packagePrefix + "." + $temp.text;
    		methodCallAnalyzer.setTypeNameOfOwnerInstance( thisTypeName );
    		modelData.addVertexData( new VertexData( thisTypeName, $temp.line, $temp.pos, BPSLTermType.CLASS) );
    	}
    	genericTypeParameterList? extendsClause? interfaceTopLevelScope)
    	
    |   ^(ENUM modifierList temp=IDENT
    	{
    		thisTypeName = packagePrefix + "." + $temp.text;
    		methodCallAnalyzer.setTypeNameOfOwnerInstance( thisTypeName );
    		modelData.addVertexData( new VertexData( thisTypeName, $temp.line, $temp.pos, BPSLTermType.CLASS) );
    	}
    	implementsClause? enumTopLevelScope)
    	
    |   ^(AT modifierList IDENT annotationTopLevelScope)
    	
    ;

extendsClause // actually 'type' for classes and 'type+' for interfaces, but this has 
              // been resolved by the parser grammar.
    :   ^(temp2=EXTENDS_CLAUSE (temp=type
    	{
    		superType = $temp.text;
    		superType = typeNameQualifier.getQualifiedTypeName(superType)[0];
    		modelData.addEdgeData( new EdgeData( $temp2.line, $temp2.pos, BPSLPredicateType.Inheritance, thisTypeName, superType) );
    	}
    	)+ )
    ;   
    
implementsClause
    :   ^(IMPLEMENTS_CLAUSE type+)
    ;
        
genericTypeParameterList
    :   ^(GENERIC_TYPE_PARAM_LIST genericTypeParameter+)
    ;

genericTypeParameter
    :   ^(IDENT bound?)
    ;
        
bound
    :   ^(EXTENDS_BOUND_LIST type+)
    ;

enumTopLevelScope
    :   ^(ENUM_TOP_LEVEL_SCOPE enumConstant+ classTopLevelScope?)
    ;
    
enumConstant
    :   ^(IDENT annotationList arguments? classTopLevelScope?)
    ;
    
    
classTopLevelScope
    :   ^(CLASS_TOP_LEVEL_SCOPE classScopeDeclarations*)
    ;
    
classScopeDeclarations
	scope
		{
			List<String> methodParameterTypes;
			String currentMethodName;
		}
	@init
		{
			currentScope = Scope.CLASS;
		}
    :   ^(CLASS_INSTANCE_INITIALIZER block)
    |   ^(CLASS_STATIC_INITIALIZER block)
    |   ^(FUNCTION_METHOD_DECL modifierList genericTypeParameterList? type temp=IDENT formalParameterList
    	{
    		try{
    			int size = $classScopeDeclarations::methodParameterTypes.size();
    			String[] parameterTypes = $classScopeDeclarations::methodParameterTypes.toArray( new String[size] );
    			String uniqueMethodName = methodResolver.getUniqueMethodName( thisTypeName + "." +  $temp.text , parameterTypes );
    			modelData.addVertexData( new VertexData( uniqueMethodName, $temp.line, $temp.pos, BPSLTermType.METHOD) );
    			modelData.addEdgeData( new EdgeData( $temp.line, $temp.pos, BPSLPredicateType.DefinedIn, uniqueMethodName, thisTypeName) );
    			$classScopeDeclarations::currentMethodName = uniqueMethodName;
    		}
    		catch( UnknownMethodException e){
    			exceptionProcessor.handleCustomException(e);	
    		}
    	}
    	arrayDeclaratorList? throwsClause?
    	{
    		currentScope = Scope.METHODBLOCK;
    	}
    	block?
    	{currentScope = Scope.CLASS; }
    	)
    |   ^(VOID_METHOD_DECL modifierList genericTypeParameterList? temp=IDENT formalParameterList
    	{
    		try{
    			int size = $classScopeDeclarations::methodParameterTypes.size();
    			String[] parameterTypes = $classScopeDeclarations::methodParameterTypes.toArray( new String[size] );
    			String uniqueMethodName = methodResolver.getUniqueMethodName( thisTypeName + "." + $temp.text , parameterTypes );
    			modelData.addVertexData( new VertexData( uniqueMethodName, $temp.line, $temp.pos, BPSLTermType.METHOD) );
    			modelData.addEdgeData( new EdgeData( $temp.line, $temp.pos, BPSLPredicateType.DefinedIn, uniqueMethodName, thisTypeName) );
    			$classScopeDeclarations::currentMethodName = uniqueMethodName;
    		}
    		catch( UnknownMethodException e){
    			exceptionProcessor.handleCustomException(e);	
    		}
    	}
    	throwsClause?
    	{ 	currentScope = Scope.METHODBLOCK;
    	}
    	block?
    	{currentScope = Scope.CLASS; }
    	)
    |   ^(temp=VAR_DECLARATION modifierList type list=variableDeclaratorList)
    	{
    		String attributeType;
    		for (String id : list ){
    			attributeType = attributeResolver.getAttributeType( id);
    			modelData.addEdgeData( new EdgeData( $temp.line, $temp.pos, BPSLPredicateType.ReferenceToOne, thisTypeName, attributeType ) );
    		}
    	}
    |   ^(CONSTRUCTOR_DECL
    	 modifierList genericTypeParameterList? formalParameterList throwsClause?
    	 { currentScope = Scope.CONSTRUCTOR; }
    	 block
    	 {currentScope = Scope.CLASS; }
    	 )
    |   typeDeclaration
    ;
    
interfaceTopLevelScope
    :   ^(INTERFACE_TOP_LEVEL_SCOPE interfaceScopeDeclarations*)
    ;
    
interfaceScopeDeclarations
    :   ^(FUNCTION_METHOD_DECL modifierList genericTypeParameterList? type IDENT formalParameterList arrayDeclaratorList? throwsClause?)
    |   ^(VOID_METHOD_DECL modifierList genericTypeParameterList? IDENT formalParameterList throwsClause?)
                         // Interface constant declarations have been switched to variable
                         // declarations by 'java.g'; the parser has already checked that
                         // there's an obligatory initializer.
    |   ^(VAR_DECLARATION modifierList type variableDeclaratorList)
    |   typeDeclaration
    ;

variableDeclaratorList returns [List<String> ids]
	@init{
		ids = new LinkedList<String>();
	}
    :   ^(VAR_DECLARATOR_LIST ( id=variableDeclarator { ids.add(id); } )+ )
    ;

variableDeclarator returns[String id]
    :   ^(VAR_DECLARATOR temp=variableDeclaratorId
    	{
    		id = $temp.text;
    	} 
    	 variableInitializer?)
    ;
    
variableDeclaratorId
    :   ^(IDENT arrayDeclaratorList?)
    ;

variableInitializer
    :   arrayInitializer
    |   expression
    ;

arrayDeclarator
    :   LBRACK RBRACK
    ;

arrayDeclaratorList
    :   ^(ARRAY_DECLARATOR_LIST ARRAY_DECLARATOR*)  
    ;
    
arrayInitializer
    :   ^(ARRAY_INITIALIZER variableInitializer*)
    ;

throwsClause
    :   ^(THROWS_CLAUSE qualifiedIdentifier+)
    ;

modifierList
    :   ^(MODIFIER_LIST modifier*)
    ;

modifier
    :   PUBLIC
    |   PROTECTED
    |   PRIVATE
    |   STATIC
    |   ABSTRACT
    |   NATIVE
    |   SYNCHRONIZED
    |   TRANSIENT
    |   VOLATILE
    |   STRICTFP
    |   localModifier
    ;

localModifierList
    :   ^(LOCAL_MODIFIER_LIST localModifier*)
    ;

localModifier
    :   FINAL
    |   annotation
    ;


type returns [String value]
    :   ^(TYPE ( ( temp1 = primitiveType { $value = temp1; } ) | ( temp2 = qualifiedTypeIdent { $value = $temp2.text; } ) )arrayDeclaratorList?)
    ;

qualifiedTypeIdent
    :   ^(QUALIFIED_TYPE_IDENT typeIdent+) 
    ;

typeIdent
    :   ^(IDENT genericTypeArgumentList?)
    ;

primitiveType returns [String type]
    :   BOOLEAN		{ type = "java.lang.Boolean"; }
    |   CHAR		{ type = "java.lang.Character"; }
    |   BYTE		{ type = "java.lang.Byte"; }
    |   SHORT		{ type = "java.lang.Short"; }
    |   INT			{ type = "java.lang.Integer"; }
    |   LONG		{ type = "java.lang.Long"; }
    |   FLOAT		{ type = "java.lang.Float"; }
    |   DOUBLE		{ type = "java.lang.Double"; }
    ;

genericTypeArgumentList
    :   ^(GENERIC_TYPE_ARG_LIST genericTypeArgument+)
    ;
    
genericTypeArgument
    :   type
    |   ^(QUESTION genericWildcardBoundType?)
    ;

genericWildcardBoundType                                                                                                                      
    :   ^(EXTENDS type)
    |   ^(SUPER type)
    ;

formalParameterList
	@init
		{
			if( currentScope == Scope.CLASS ){ $classScopeDeclarations::methodParameterTypes = new ArrayList<String>(); }
		}

    :   ^(FORMAL_PARAM_LIST formalParameterStandardDecl* formalParameterVarargDecl?) 
    ;
    
formalParameterStandardDecl
    :   ^(FORMAL_PARAM_STD_DECL localModifierList temp=type
    	{
    		if( currentScope == Scope.CLASS ){
    			//currentScope == Scope.CLASS guarantees the the invocation of this method is from a methodDefinition nd not from a catchClause in a try-catch construct of a METHODBLOCK
    			String[] qualifiedName = typeNameQualifier.getQualifiedTypeName( $temp.value );
    			$classScopeDeclarations::methodParameterTypes.add( qualifiedName[0] );
    		}
    	}
    	variableDeclaratorId)
    ;
    
formalParameterVarargDecl
    :   ^(FORMAL_PARAM_VARARG_DECL localModifierList type variableDeclaratorId)
    ;
    
qualifiedIdentifier
    :   IDENT
    |   ^(DOT qualifiedIdentifier IDENT)
    ;
    
// ANNOTATIONS

annotationList
    :   ^(ANNOTATION_LIST annotation*)
    ;

annotation
    :   ^(AT qualifiedIdentifier annotationInit?)
    ;
    
annotationInit
    :   ^(ANNOTATION_INIT_BLOCK annotationInitializers)
    ;

annotationInitializers
    :   ^(ANNOTATION_INIT_KEY_LIST annotationInitializer+)
    |   ^(ANNOTATION_INIT_DEFAULT_KEY annotationElementValue)
    ;
    
annotationInitializer
    :   ^(IDENT annotationElementValue)
    ;
    
annotationElementValue
    :   ^(ANNOTATION_INIT_ARRAY_ELEMENT annotationElementValue*)
    |   annotation
    |   expression
    ;
    
annotationTopLevelScope
    :   ^(ANNOTATION_TOP_LEVEL_SCOPE annotationScopeDeclarations*)
    ;
    
annotationScopeDeclarations
    :   ^(ANNOTATION_METHOD_DECL modifierList type IDENT annotationDefaultValue?)
    |   ^(VAR_DECLARATION modifierList type variableDeclaratorList)
    |   typeDeclaration
    ;
    
annotationDefaultValue
    :   ^(DEFAULT annotationElementValue)
    ;

// STATEMENTS / BLOCKS

block
	scope
		{
			AttributeTypeResolver localVars;
		}
	@init{
			$block::localVars = new AttributeTypeResolver("local");
	}
    :   ^(BLOCK_SCOPE blockStatement*)
    ;
    
blockStatement
    :   localVariableDeclaration
    |   typeDeclaration
    |   statement
    ;
    
localVariableDeclaration
    :   ^(VAR_DECLARATION localModifierList temp=type list=variableDeclaratorList)
    	 {
    		String[] qualifiedTypeName = typeNameQualifier.getQualifiedTypeName( $temp.value );	
    		String varType = null;
    		if( qualifiedTypeName != null ) varType = qualifiedTypeName[0];
    		for( String id : list ){
    			$block::localVars.addAttribute( id, varType );
    		}
    	}
    ;
    
        
statement
    :   block
    |   ^(ASSERT expression expression?)
    |   ^(IF parenthesizedExpression statement statement?)
    |   ^(FOR forInit forCondition forUpdater statement)
    |   ^(FOR_EACH localModifierList type IDENT expression statement) 
    |   ^(WHILE parenthesizedExpression statement)
    |   ^(DO statement parenthesizedExpression)
    |   ^(TRY block catches? block?)  // The second optional block is the optional finally block.
    |   ^(SWITCH parenthesizedExpression switchBlockLabels)
    |   ^(SYNCHRONIZED parenthesizedExpression block)
    |   ^(RETURN expression?)
    |   ^(THROW expression)
    |   ^(BREAK IDENT?)
    |   ^(CONTINUE IDENT?)
    |   ^(LABELED_STATEMENT IDENT statement)
    |   expression
    |   SEMI // Empty statement.
    ;
        
catches
    :   ^(CATCH_CLAUSE_LIST catchClause+)
    ;
    
catchClause
    :   ^(CATCH formalParameterStandardDecl block)
    ;

switchBlockLabels
    :   ^(SWITCH_BLOCK_LABEL_LIST switchCaseLabel* switchDefaultLabel? switchCaseLabel*)
    ;
        
switchCaseLabel
    :   ^(CASE expression blockStatement*)
    ;
    
switchDefaultLabel
    :   ^(DEFAULT blockStatement*)
    ;
    
forInit
    :   ^(FOR_INIT (localVariableDeclaration | expression*)?)
    ;
    
forCondition
    :   ^(FOR_CONDITION expression?)
    ;
    
forUpdater
    :   ^(FOR_UPDATE expression*)
    ;
    
// EXPRESSIONS

parenthesizedExpression
    :   ^(PARENTESIZED_EXPR expression)
    ;
    
expression returns [String type]
    :   ^(EXPR temp=expr)	{ type = temp; }
    ;

expr	returns [String type]
    :   ^(ASSIGN temp1=expr temp2=expr)			{ type = ExpressionTypeConverting.getResultType( temp1, temp2 ); }
    |   ^(PLUS_ASSIGN temp1=expr temp2=expr)	{ type = ExpressionTypeConverting.getResultType( temp1, temp2 ); }
    |   ^(MINUS_ASSIGN temp1=expr temp2=expr)	{ type = ExpressionTypeConverting.getResultType( temp1, temp2 ); }
    |   ^(STAR_ASSIGN temp1=expr temp2=expr)	{ type = ExpressionTypeConverting.getResultType( temp1, temp2 ); }
    |   ^(DIV_ASSIGN temp1=expr temp2=expr)		{ type = ExpressionTypeConverting.getResultType( temp1, temp2 ); }
    |   ^(AND_ASSIGN expr expr)					{ type= "java.lang.Boolean"; }
    |   ^(OR_ASSIGN expr expr)					{ type= "java.lang.Boolean";}
    |   ^(XOR_ASSIGN expr expr)					{ type= "java.lang.Boolean"; }
    |   ^(MOD_ASSIGN expr expr)					{ type= "java.lanf.Integer"; }
    |   ^(BIT_SHIFT_RIGHT_ASSIGN temp1=expr temp2=expr)	{ type = ExpressionTypeConverting.getResultType( temp1, temp2 ); }
    |   ^(SHIFT_RIGHT_ASSIGN temp1=expr temp2=expr)		{ type = ExpressionTypeConverting.getResultType( temp1, temp2 ); }
    |   ^(SHIFT_LEFT_ASSIGN temp1=expr temp2=expr)		{ type = ExpressionTypeConverting.getResultType( temp1, temp2 ); }
    |   ^(QUESTION expr expr expr)				{ type= "null";}
    |   ^(LOGICAL_OR expr expr)					{ type= "java.lang.Boolean"; }
    |   ^(LOGICAL_AND expr expr)				{ type= "java.lang.Boolean"; }
    |   ^(OR expr expr)							{ type= "java.lang.Boolean"; }
    |   ^(XOR expr expr)						{ type= "java.lang.Boolean"; }
    |   ^(AND expr expr)						{ type= "java.lang.Boolean"; }
    |   ^(EQUAL expr expr)						{ type= "java.lang.Boolean"; }
    |   ^(NOT_EQUAL expr expr)					{ type= "java.lang.Boolean"; }
    |   ^(INSTANCEOF expr type)					{ type= "java.lang.Boolean"; }
    |   ^(LESS_OR_EQUAL expr expr)				{ type= "java.lang.Boolean"; }
    |   ^(GREATER_OR_EQUAL temp1=expr expr)			{ type= "java.lang.Boolean"; }
    |   ^(BIT_SHIFT_RIGHT temp1=expr temp2=expr){ type = ExpressionTypeConverting.getResultType( temp1, temp2 ); }		
    |   ^(SHIFT_RIGHT temp1=expr temp2=expr)	{ type = ExpressionTypeConverting.getResultType( temp1, temp2 ); }
    |   ^(GREATER_THAN expr expr)				{ type= "java.lang.Boolean"; }
    |   ^(SHIFT_LEFT temp1=expr temp2=expr)		{ type = ExpressionTypeConverting.getResultType( temp1, temp2 ); }
    |   ^(LESS_THAN expr expr)					{ type= "java.lang.Boolean"; }
    |   ^(PLUS temp1=expr temp2=expr)			{ type = ExpressionTypeConverting.getResultType( temp1, temp2 ); }
    |   ^(MINUS temp1=expr temp2=expr)			{ type = ExpressionTypeConverting.getResultType( temp1, temp2 ); }
    |   ^(STAR temp1=expr temp2=expr)			{ type = ExpressionTypeConverting.getResultType( temp1, temp2 ); }
    |   ^(DIV temp1=expr temp2=expr)			{ type = ExpressionTypeConverting.getResultType( temp1, temp2 ); }
    |   ^(MOD expr expr)						{ type= "java.lang.Integer"; }
    |   ^(UNARY_PLUS temp1=expr)				{ type = temp1; }	
    |   ^(UNARY_MINUS temp1=expr)				{ type = temp1; }
    |   ^(PRE_INC temp1=expr)					{ type = temp1; }
    |   ^(PRE_DEC temp1=expr)					{ type = temp1; }
    |   ^(POST_INC temp1=expr)					{ type = temp1; }
    |   ^(POST_DEC temp1=expr)					{ type = temp1; }
    |   ^(NOT expr)								{ type= "java.lang.Boolean"; }
    |   ^(LOGICAL_NOT expr)						{ type= "java.lang.Boolean"; }
    |   ^(CAST_EXPR temp=type expr)				{ type= typeNameQualifier.getQualifiedTypeName( $temp.value )[0]; }
    |   temp1=primaryExpression					{ type = temp1; }
    ;
    
primaryExpression returns [String type]
    :   ^(  temp=DOT { if( methodCallAnalyzer.isActivated() ){ methodCallAnalyzer.addMethodCallFragment( $temp.text); } }
            (   primaryExpression 
                (   temp=IDENT		{ if( methodCallAnalyzer.isActivated() ){ methodCallAnalyzer.addMethodCallFragment( $temp.text ); } }	  
                |   temp=THIS   	{ if( methodCallAnalyzer.isActivated() ){ methodCallAnalyzer.addMethodCallFragment( $temp.text ); } }       
                |   temp=SUPER  	{ if( methodCallAnalyzer.isActivated() ){ methodCallAnalyzer.addMethodCallFragment( $temp.text ); } }      
                |   innerNewExpression
                |   CLASS         
                )
            |   primitiveType CLASS
            |   VOID CLASS
            )					
        )
    |   parenthesizedExpression
    |   temp=IDENT
    	{
    		if( methodCallAnalyzer.isActivated() &&  ! methodCallAnalyzer.isArgumentCollecting() ){
    			methodCallAnalyzer.addMethodCallFragment( $temp.text );
    		}
    		else{
    			String value =  $block::localVars.getAttributeType( $temp.text );
    			if( value == null ){
    				value = attributeResolver.getAttributeType( $temp.text );
    			}
    			type = value;
    		}
    	}
    |   ^(meth=METHOD_CALL { methodCallAnalyzer.activate( $block::localVars ); } primaryExpression genericTypeArgumentList? { methodCallAnalyzer.collectArguments(); }argumentTypes=arguments
    	{ 
    		try{
    			methodCallAnalyzer.setArgumentTypes( argumentTypes.toArray( new String[ argumentTypes.size() ] ) );
    			String calledMethod = methodCallAnalyzer.getUniqueMethodName();
    			String objectType = methodCallAnalyzer.getObjectType();
    			methodCallAnalyzer.testPrint();
    			System.out.println( calledMethod );
    			if( currentScope == Scope.METHODBLOCK ){
    				modelData.addEdgeData( new EdgeData( $meth.line, $meth.pos, BPSLPredicateType.Invocation, $classScopeDeclarations::currentMethodName, calledMethod) );
    				modelData.addEdgeData( new EdgeData( $meth.line, $meth.pos, BPSLPredicateType.Invocation, thisTypeName, calledMethod) );
    				modelData.addEdgeData( new EdgeData( $meth.line, $meth.pos, BPSLPredicateType.Invocation, $classScopeDeclarations::currentMethodName, objectType) );
    				modelData.addEdgeData( new EdgeData( $meth.line, $meth.pos, BPSLPredicateType.Invocation, thisTypeName, objectType) );	
    			}
    		}
    		catch( UnknownMethodException e){
    			exceptionProcessor.handleCustomException(e);	
    		}
    		finally{	
    			methodCallAnalyzer.deactivate();
    		} 
    	}
    	) //AST of a methodcall: primaryExpression will match "objectName.methodName" , arguments maps the paramenters ex "x", "y" in "object.method(x,y)
    |   explicitConstructorCall
    |   ^(ARRAY_ELEMENT_ACCESS primaryExpression expression)
    |   temp2=literal { type = $temp2.type; }
    |   newExpression	{ type = "null"; }
    |   THIS		{ type = thisTypeName; }
    |   arrayTypeDeclarator	{ type = "null"; }
    |   SUPER
    	{
    		if (superType == null)	type = "java.lang.Object";
    		else					type = superType;
    	}
    ;
    
explicitConstructorCall
    :   ^(THIS_CONSTRUCTOR_CALL genericTypeArgumentList? arguments)
    |   ^(SUPER_CONSTRUCTOR_CALL primaryExpression? genericTypeArgumentList? arguments)
    ;

arrayTypeDeclarator
    :   ^(ARRAY_DECLARATOR (arrayTypeDeclarator | qualifiedIdentifier | primitiveType))
    ;

newExpression
    :   ^(  STATIC_ARRAY_CREATOR
            (   primitiveType newArrayConstruction
            |   genericTypeArgumentList? qualifiedTypeIdent newArrayConstruction
            )
        )
    |   ^(CLASS_CONSTRUCTOR_CALL genericTypeArgumentList? qualifiedTypeIdent arguments classTopLevelScope?)
    ;

innerNewExpression // something like 'InnerType innerType = outer.new InnerType();'
    :   ^(CLASS_CONSTRUCTOR_CALL genericTypeArgumentList? IDENT arguments classTopLevelScope?)
    ;
 
newArrayConstruction
    :   arrayDeclaratorList arrayInitializer
    |   expression+ arrayDeclaratorList?
    ;

literal returns [String type]
    :   HEX_LITERAL				{ $type = "java.lang.Integer"; }
    |   OCTAL_LITERAL			{ $type = "java.lang.Integer"; }
    |   DECIMAL_LITERAL			{ $type = "java.lang.Integer"; }
    |   FLOATING_POINT_LITERAL 	{ $type = "java.lang.Double"; }
    |   CHARACTER_LITERAL		{ $type = "java.lang.Character"; }
    |   STRING_LITERAL			{ $type = "java.lang.String"; }
    |   TRUE					{ $type = "java.lang.Boolean"; }
    |   FALSE					{ $type = "java.lang.Boolean"; }
    |   NULL					{ $type = "null"; }
    ;


arguments returns [List<String> argumentTypes]
	@init {
		argumentTypes = new LinkedList<String>();
	}
    :   ^(ARGUMENT_LIST ( temp=expression 
    	{
    	 	argumentTypes.add( typeNameQualifier.getQualifiedTypeName( temp )[0] );
    		
    	}
    	)* )
    ;

